var _ = require('lodash');

// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)

var baseEasings = {};

_.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( name, i ) {
    baseEasings[ name ] = function( p ) {
        return Math.pow( p, i + 2 );
    };
});

_.extend( baseEasings, {
    "Sine": function( p ) {
        return 1 - Math.cos( p * Math.PI / 2 );
    },
    "Circ": function( p ) {
        return 1 - Math.sqrt( 1 - p * p );
    },
    "Elastic": function( p ) {
        return p === 0 || p === 1 ? p :
        -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
    },
    "Back": function( p ) {
        return p * p * ( 3 * p - 2 );
    },
    "Bounce": function( p ) {
        var pow2,
            bounce = 4;

        while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
        return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
    }
});

var easing = {
    "linear": function(p) {
        return p;
    }
};

_.each( baseEasings, function( easeIn, name ) {
    easing[ "easeIn" + name ] = easeIn;
    easing[ "easeOut" + name ] = function( p ) {
        return 1 - easeIn( 1 - p );
    };
    easing[ "easeInOut" + name ] = function( p ) {
        return p < 0.5 ?
        easeIn( p * 2 ) / 2 :
        1 - easeIn( p * -2 + 2 ) / 2;
    };
});

module.exports = function(func, duration, ease, callback) {
    if (_.isFunction(ease)) {
        callback = ease;
        ease = 'linear';
    } else if (_.isEmpty(ease)) {
        ease = 'linear';
    }
    var start = Date.now();
    var tick = function() {
        var remain = Math.max(0, start + duration - Date.now()),
            percent = 1 - easing[ease](remain / duration);
        func(percent);
        if (percent !== 1) {
            requestAnimationFrame(tick);
        } else {
            callback && callback();
        }
    };
    tick();
};